昨天(Day 2)我們扮演了 PM,把專案的需求、目標和架構拆解清楚。
今天開始,就要進入實作階段了,專案程式碼已放在 GitHub,之後如果有 Demo
也都會放在上面,有興趣的讀者可以自行 fork
來改寫、實驗。
在正式進入 RAG Pipeline 之前,我們需要一個 可重現、乾淨的開發環境。
這裡我們提供兩條路徑,讀者可以依照自己的需求選擇:
接下來的文章裡,大多數 Demo 都能直接在 路徑 A 下完成;
如果你需要更穩定的團隊環境或部署到伺服器,再切換到 路徑 B 即可。
pip
更適合需要科學計算、GPU 支援的場景)。day03_environments_prepare/
├── docker/
│ └── Dockerfile
├── env/
│ └── environment.yml
├── src/
│ └── app.py
└── README.md
docker/Dockerfile
:定義環境。env/environment.yml
:Conda 環境需求。src/app.py
:主程式。README.md
:專案說明。這個專案需要一個乾淨的 Python 環境,我們推薦使用 Miniforge(由社群維護的 Conda 發行版,比 Anaconda 輕量,而且預設支援 conda-forge
channel)。因為筆者自己的電腦是 Macbook Air,這邊僅列出筆者自己的安裝步驟:
brew install --cask miniforge
echo 'export PATH="$HOME/miniforge3/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
conda --version
env/environment.yml
:name: day03_environments
channels:
- defaults
- conda-forge
dependencies:
- python=3.10
- pip
- pip:
- fastapi
- uvicorn
- langchain
- openai
- weaviate-client
- jupyter
conda env create -f env/environment.yml
conda activate day03_environments
💡 如果已經有其他 Conda 版本(例如 Anaconda 或 Miniconda),也能沿用,只要確保能成功建立
day03_environments
環境即可。
Python 其實內建了 venv
,也能做環境隔離,那為什麼我們要用 Conda?
environment.yml
能完整描述環境,比 requirements.txt
更穩定功能 | venv/virtualenv | conda |
---|---|---|
Python 版本管理 | ❌ 系統決定 | ✅ conda create -n py39 python=3.9 |
安裝 C/C++/GPU 套件 | ❌ 需手動編譯 | ✅ 預先提供 binary,安裝穩定 |
跨語言支援 | ❌ 只管 Python | ✅ 支援 R, Julia, C, CUDA 等 |
環境描述檔 | requirements.txt | environment.yml(更完整) |
輕量程度 | ✅ 超輕量 | ❌ 稍微重一些,但更完整 |
後面每一天的文章,我會建議讀者把 conda
環境獨立建置和啟動。
(這邊依照自己習慣的方式開發即可,也可以先不用容器化)
建立 docker/Dockerfile
:
FROM continuumio/miniconda3:23.5.2-0
WORKDIR /app
# 複製環境需求
COPY env/environment.yml .
# 建立 conda 環境
RUN conda env create -f environment.yml
# 預設啟用 conda 環境
SHELL ["conda", "run", "-n", "day03_environments", "/bin/bash", "-c"]
# 複製專案程式碼
COPY src/ ./src
# 預設啟動 FastAPI app
CMD ["conda", "run", "-n", "day03_environments", "uvicorn", "src.app:app", "--host", "0.0.0.0", "--port", "8000"]
建立環境所需套件 env/environment.yml
:
name: day03_environments
channels:
- defaults
- conda-forge
dependencies:
- python=3.10
- pip
- pip:
- fastapi
- uvicorn
- langchain
- weaviate-client
- jupyter
- openai
- python-dotenv
新增一個 src/app.py
:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "LLMOps!"}
建置與執行 (用 podman
也可):
docker build -t day03_environments -f docker/Dockerfile .
docker run -it --rm -p 8000:8000 day03_environments
如果想要先build 再掛載 app.py
進去,方便隨時修改的話,可以用 `docker run 的方式:
docker build --build-arg MODE=dev -t day03_environments -f docker/Dockerfile .
docker run -it --rm -p 8000:8000 \
-v "$(pwd)/src:/app/src" \
day03_environments
Hot-reload 版的 Dockerfile :
FROM continuumio/miniconda3:23.5.2-0
# ── 基本設定 ────────────────────────────────────────────────
WORKDIR /app
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONPATH=/app
# 可用 MODE=dev 啟用 --reload;預設 prod
ARG MODE=prod
ENV MODE=${MODE}
# ── 安裝專案環境 ────────────────────────────────────────────
COPY env/environment.yml .
RUN conda env create -f environment.yml
# 之後的 RUN/CMD/ENTRYPOINT 都在指定環境內執行
SHELL ["conda", "run", "-n", "day03_environments", "/bin/bash", "-c"]
# ── 複製程式碼(開發時可用 volume 掛載覆蓋) ───────────────
COPY src/ ./src
# ── 對外連接埠 ─────────────────────────────────────────────
EXPOSE 8000
# ── 依據 MODE 切換啟動參數 ─────────────────────────────────
# prod: 不加 --reload;dev: 加 --reload
CMD if [ "$MODE" = "dev" ]; then \
uvicorn --app-dir /app src.app:app --host 0.0.0.0 --port 8000 --reload; \
else \
uvicorn --app-dir /app src.app:app --host 0.0.0.0 --port 8000; \
fi
測試環境是否正常:
啟動後訪問 `http://localhost:8000/` → 看到 `{"Hello": "LLMOps!"}` 表示環境成功。
接下來的測試,會需要使用 OpenAI API,需要先到 OpenAI API Keys 頁面 建立一組 Key。
建立後,把它存到 .env
檔裡:
export OPENAI_API_KEY=sk-xxxxxxx
在程式裡透過套件呼叫 OpenAI API:
from openai import OpenAI
import os
from dotenv import load_dotenv
load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
max_tokens
,例如 Demo 設定在 300 tokens 以內。⚠️ 小提醒
本系列中的程式碼與 Demo,已經盡量使用簡化、節省費用的設定(例如限制max_tokens
、選用較便宜的模型),讓大家能安心跟著練習。
不過,如果讀者在實作時輸入大量資料,或改用高單價模型,可能會導致 API 成本比預期高。
這部分請讀者自行調整的使用情境,作者無法保證費用一定維持在同一水平。
建議大家在開始測試前,先到 Usage Limits 設定好每月預算上限,這樣就能更放心地探索,不怕花費失控。 🌱
本系列的測試大部分會示範 OpenAI API。讀者如果想嘗試 其他 LLM 供應商(例如 Anthropic Claude、Google Gemini、Mistral、AWS Bedrock、Azure OpenAI Service 等),也完全可行。本系列的程式碼示範只是「範例骨架」,讀者可以替換成自己偏好的模型與 API 🙂
⚠️ 只需要注意:
API 介面是否相容
model
、max_tokens
、temperature
)在不同平台名稱可能略有差異。計費方式
部署位置
我們先來寫個測試程式看看能不能產生 Embedding。OpenAI 的 Embedding API 丟進去一串文字,他會回傳一串浮點數,代表某種抽象的語意特徵,語意相近的文字,回傳的浮點數也會相近。詳細的資訊會在第五天做介紹,這邊我們先試著呼叫 API 就好。
# 用 conda 創建一個新的專案環境
❯ conda env create -f ./env/environment.yml
# 啟動專案環境
❯ conda activate day03_environments
# 把 OpenAI API key 寫到環境變數裡
#(僅本次 session 有效,若要長期使用可寫進 `.env` 或 shell 啟動檔)
export OPENAI_API_KEY=sk-xxxxxxx
# 呼叫測試程式碼
❯ python test_embedding.py
Cosine similarity: 0.2016753894756205
test_scripts/test_embedding.py
:
from openai import OpenAI
from dotenv import load_dotenv
import numpy as np
import os
load_dotenv()
client = OpenAI()
# 讀取 .env 檔案裡面的環境變數
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise ValueError("沒有找到 OPENAI_API_KEY,請檢察環境變數!")
# 比較這兩個字串的相近程度
texts = ["鐵人賽好熱血", "我想寫一個 QA Bot"]
embeddings = [client.embeddings.create(model="text-embedding-3-small", input=t).data[0].embedding for t in texts]
similarity = np.dot(embeddings[0], embeddings[1]) / (np.linalg.norm(embeddings[0]) * np.linalg.norm(embeddings[1]))
print("Cosine similarity:", similarity)
執行後應該會輸出一個 0–1 之間的數值,代表兩句話的相似度。
最後,我們用 FastAPI 做一個最小的 QA API,部署圖(Deployment Diagram)如下:
修改 src/app.py
,添加完整的程式碼:
from fastapi import FastAPI
from openai import OpenAI
from dotenv import load_dotenv
import os
# 載入 .env
load_dotenv()
# 讀取金鑰
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
raise RuntimeError("❌ 找不到 OPENAI_API_KEY,請確認 .env 或環境變數設定")
app = FastAPI()
client = OpenAI(api_key=api_key)
@app.get("/")
def health():
return {"ok": True}
# 正式情境我們會用 POST & JSON Body 來處理需要編碼的字串
@app.get("/ask")
def ask(q: str):
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": q}]
)
return {"question": q, "answer": resp.choices[0].message.content}
docker run 啟動容器:
# 先把 API key 寫進去設定檔案
echo "OPENAI_API_KEY=sk-proj-XXX(你的 OpenAI API key)" >> .env
# 啟動容器
docker run -it --rm -p 8000:8000 \
-v "$(pwd)/src:/app/src" \
-v "$(pwd)/.env:/app/.env:ro" \
day03_environments
或是用 uvicorn
跑起來 :
uvicorn src.app:app --reload --port 8000
測試:
# 繁體中文要先編碼,否則會被判定成非法的 HTTP request
curl -G --data-urlencode "q=什麼是LLMOps?請用繁體中文回答我" http://localhost:8000/ask
結果:
{"question":"什麼是LLMOps?請用繁體中文回答我","answer":"LLMOps(Large Language Model Operations)是一種針對大型語言模型(LLM)進行管理和運營的綜合方法和流程。隨著大型語言模型的發展和應用越來越廣泛,LLMOps的概念也應運而生,旨在提高這些模型的開發、部署和維護效率。\n\nLLMOps包括以下幾個關鍵方面:\n\n1. **模型管理**:針對大型語言模型的版本控制和訓練流程的管理,確保模型的可靠性和可重複性。\n\n2. **數據管理**:合理管理用於訓練和評估模型的數據集,包括數據的清理、標註以及更新。\n\n3. **環境配置**:建立和維護運行大型語言模型所需的計算環境和基礎設施,確保模型能在最佳性能下運行。\n\n4. **監控與評估**:持續監控模型在實際應用中的表現,及時識別和解決問題,以確保模型的穩定性和準確性。\n\n5. **協同合作**:促進跨團隊間的協作,確保開發、運營和產品團隊之間的有效溝通與合作。\n\nLLMOps的目的是提高大型語言模型的可用性和效能,使企業和組織能夠更有效地利用這些強大的工具來解決實際問題。"}
🎉 恭喜!我們已經有了第一個能回答問題的小助手。